<template>
	<div class="container">
		<div class="left-board">
			<div class="logo-wrapper">
				<div class="logo">流程表单</div>
			</div>
			<el-scrollbar class="left-scrollbar">
				<!-- 左边：表单项 -->
				<div class="components-list">
					<div
						v-for="(item, listIndex) in leftComponents"
						:key="listIndex"
					>
						<div class="components-title">
							<svg-icon icon-class="component" />
							{{ item.title }}
						</div>
						<draggable
							class="components-draggable"
							:list="item.list"
							:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
							:clone="cloneComponent"
							draggable=".components-item"
							:sort="false"
							@end="onEnd"
						>
							<div
								v-for="(element, index) in item.list"
								:key="index"
								class="components-item"
								@click="addComponent(element)"
							>
								<div class="components-body">
									<svg-icon :icon-class="element.__config__.tagIcon" />
									{{ element.__config__.label }}
								</div>
							</div>
						</draggable>
					</div>

					<!-- 左边：动态表单 -->
					<el-form
						ref="form"
						:model="form"
						:rules="rules"
						label-width="80px"
					>
						<el-form-item
							label="表单名"
							prop="name"
						>
							<el-input
								v-model="form.name"
								placeholder="请输入表单名"
							/>
						</el-form-item>
						<el-form-item
							label="开启状态"
							prop="status"
						>
							<el-radio-group v-model="form.status">
								<el-radio
									v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
									:key="dict.value"
									:label="parseInt(dict.value)"
									>{{ dict.label }}</el-radio
								>
							</el-radio-group>
						</el-form-item>
						<el-form-item
							label="备注"
							prop="remark"
						>
							<el-input
								type="textarea"
								v-model="form.remark"
								placeholder="请输入备注"
							/>
						</el-form-item>
					</el-form>
				</div>
			</el-scrollbar>
		</div>

		<div class="center-board">
			<div class="action-bar">
				<el-button
					icon="el-icon-check"
					type="text"
					@click="save"
					>保存</el-button
				>
				<!--        <el-button icon="el-icon-video-play" type="text" @click="run">-->
				<!--          运行-->
				<!--        </el-button>-->
				<el-button
					icon="el-icon-view"
					type="text"
					@click="showJson"
				>
					查看json
				</el-button>
				<!--        <el-button icon="el-icon-download" type="text" @click="download">-->
				<!--          导出vue文件-->
				<!--        </el-button>-->
				<!--        <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">-->
				<!--          复制代码-->
				<!--        </el-button>-->
				<el-button
					class="delete-btn"
					icon="el-icon-delete"
					type="text"
					@click="empty"
				>
					清空
				</el-button>
			</div>

			<!-- 中间，表单项 -->
			<el-scrollbar class="center-scrollbar">
				<el-row
					class="center-board-row"
					:gutter="formConf.gutter"
				>
					<el-form
						:size="formConf.size"
						:label-position="formConf.labelPosition"
						:disabled="formConf.disabled"
						:label-width="formConf.labelWidth + 'px'"
					>
						<draggable
							class="drawing-board"
							:list="drawingList"
							:animation="340"
							group="componentsGroup"
						>
							<draggable-item
								v-for="(item, index) in drawingList"
								:key="item.renderKey"
								:drawing-list="drawingList"
								:current-item="item"
								:index="index"
								:active-id="activeId"
								:form-conf="formConf"
								@activeItem="activeFormItem"
								@copyItem="drawingItemCopy"
								@deleteItem="drawingItemDelete"
							/>
						</draggable>
						<div
							v-show="!drawingList.length"
							class="empty-info"
						>
							从左侧拖入或点选组件进行表单设计
						</div>
					</el-form>
				</el-row>
			</el-scrollbar>
		</div>

		<!-- 右边：组件属性/表单属性 -->
		<right-panel
			:active-data="activeData"
			:form-conf="formConf"
			:show-field="!!drawingList.length"
			@tag-change="tagChange"
			@fetch-data="fetchData"
		/>

		<!--    <form-drawer-->
		<!--      :visible.sync="drawerVisible"-->
		<!--      :form-data="formData"-->
		<!--      size="100%"-->
		<!--      :generate-conf="generateConf"-->
		<!--    />-->

		<json-drawer
			size="60%"
			:visible.sync="jsonDrawerVisible"
			:json-str="JSON.stringify(formData)"
			@refresh="refreshJson"
		/>

		<!--    <code-type-dialog-->
		<!--      :visible.sync="dialogVisible"-->
		<!--      title="选择生成类型"-->
		<!--      :show-file-name="showFileName"-->
		<!--      @confirm="generate"-->
		<!--    />-->

		<!--    <input id="copyNode" type="hidden">-->
	</div>
</template>

<script>
import draggable from 'vuedraggable';
import { debounce } from 'throttle-debounce';
import { saveAs } from 'file-saver';
import ClipboardJS from 'clipboard';
import render from '@/components/render/render';
import FormDrawer from '@/views/infra/build/FormDrawer';
import JsonDrawer from '@/views/infra/build/JsonDrawer';
import RightPanel from '@/views/infra/build/RightPanel';
import {
	inputComponents,
	selectComponents,
	layoutComponents,
	formConf,
} from '@/components/generator/config';
import { beautifierConf, titleCase, deepClone, isObjectObject } from '@/utils';
import { makeUpHtml, vueTemplate, vueScript, cssStyle } from '@/components/generator/html';
import { makeUpJs } from '@/components/generator/js';
import { makeUpCss } from '@/components/generator/css';
import drawingDefalut from '@/components/generator/drawingDefalut';
import logo from '@/assets/logo/logo.png';
import CodeTypeDialog from '@/views/infra/build/CodeTypeDialog';
import DraggableItem from '@/views/infra/build/DraggableItem';
import {
	getDrawingList,
	saveDrawingList,
	getIdGlobal,
	saveIdGlobal,
	getFormConf,
} from '@/utils/db';
import loadBeautifier from '@/utils/loadBeautifier';
import { CommonStatusEnum } from '@/utils/constants';
import { createForm, getForm, updateForm } from '@/api/bpm/form';
import { decodeFields } from '@/utils/formGenerator';

let beautifier;
const emptyActiveData = { style: {}, autosize: {} };
let oldActiveId;
let tempActiveData;
const drawingListInDB = getDrawingList();
const formConfInDB = getFormConf();
const idGlobal = getIdGlobal();

export default {
	components: {
		draggable,
		render,
		FormDrawer,
		JsonDrawer,
		RightPanel,
		CodeTypeDialog,
		DraggableItem,
	},
	data() {
		return {
			logo,
			idGlobal,
			formConf,
			inputComponents,
			selectComponents,
			layoutComponents,
			labelWidth: 100,
			// drawingList: drawingDefalut,
			drawingData: {}, // 生成后的表单数据
			activeId: drawingDefalut[0].__config__.formId,

			drawingList: [], // 表单项的数组
			// activeId: undefined,
			// activeData: {},

			drawerVisible: false,
			formData: {},
			dialogVisible: false,
			jsonDrawerVisible: false,
			generateConf: null,
			showFileName: false,
			activeData: drawingDefalut[0], // 右边编辑器激活的表单项
			saveDrawingListDebounce: debounce(340, saveDrawingList),
			saveIdGlobalDebounce: debounce(340, saveIdGlobal),
			leftComponents: [
				{
					title: '输入型组件',
					list: inputComponents,
				},
				{
					title: '选择型组件',
					list: selectComponents,
				},
				{
					title: '布局型组件',
					list: layoutComponents,
				},
			],

			// 表单参数
			form: {
				status: CommonStatusEnum.ENABLE,
			},
			// 表单校验
			rules: {
				name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
				status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }],
			},
		};
	},
	computed: {},
	watch: {
		// eslint-disable-next-line func-names
		'activeData.__config__.label': function (val, oldVal) {
			if (
				this.activeData.placeholder === undefined ||
				!this.activeData.__config__.tag ||
				oldActiveId !== this.activeId
			) {
				return;
			}
			this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val;
		},
		activeId: {
			handler(val) {
				oldActiveId = val;
			},
			immediate: true,
		},
		drawingList: {
			handler(val) {
				this.saveDrawingListDebounce(val);
				if (val.length === 0) this.idGlobal = 100;
			},
			deep: true,
		},
		idGlobal: {
			handler(val) {
				this.saveIdGlobalDebounce(val);
			},
			immediate: true,
		},
	},
	mounted() {
		// 【add by 中赋能运维管理系统】不读缓存
		// if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
		//   this.drawingList = drawingListInDB
		// } else {
		//   this.drawingList = drawingDefalut
		// }
		// this.activeFormItem(this.drawingList[0])
		// if (formConfInDB) {
		//   this.formConf = formConfInDB
		// }
		loadBeautifier(btf => {
			beautifier = btf;
		});
		const clipboard = new ClipboardJS('#copyNode', {
			text: trigger => {
				const codeStr = this.generateCode();
				this.$notify({
					title: '成功',
					message: '代码已复制到剪切板，可粘贴。',
					type: 'success',
				});
				return codeStr;
			},
		});
		clipboard.on('error', e => {
			this.$message.error('代码复制失败');
		});
	},
	created() {
		// 读取表单配置
		const formId = this.$route.query && this.$route.query.formId;
		if (formId) {
			getForm(formId).then(response => {
				const data = response.data;
				this.form = {
					id: data.id,
					name: data.name,
					status: data.status,
					remark: data.remark,
				};
				this.formConf = JSON.parse(data.conf);
				this.drawingList = decodeFields(data.fields);
				// 设置激活的表单项
				this.activeData = this.drawingList[0];
				this.activeId = this.activeData.__config__.formId;
				// 设置 idGlobal，避免重复
				this.idGlobal += this.drawingList.length;
			});
		}
	},
	methods: {
		setObjectValueReduce(obj, strKeys, data) {
			const arr = strKeys.split('.');
			arr.reduce((pre, item, i) => {
				if (arr.length === i + 1) {
					pre[item] = data;
				} else if (!isObjectObject(pre[item])) {
					pre[item] = {};
				}
				return pre[item];
			}, obj);
		},
		setRespData(component, resp) {
			const { dataPath, renderKey, dataConsumer } = component.__config__;
			if (!dataPath || !dataConsumer) return;
			const respData = dataPath.split('.').reduce((pre, item) => pre[item], resp);

			// 将请求回来的数据，赋值到指定属性。
			// 以el-tabel为例，根据Element文档，应该将数据赋值给el-tabel的data属性，所以dataConsumer的值应为'data';
			// 此时赋值代码可写成 component[dataConsumer] = respData；
			// 但为支持更深层级的赋值（如：dataConsumer的值为'options.data'）,使用setObjectValueReduce
			this.setObjectValueReduce(component, dataConsumer, respData);
			const i = this.drawingList.findIndex(item => item.__config__.renderKey === renderKey);
			if (i > -1) this.$set(this.drawingList, i, component);
		},
		fetchData(component) {
			const { dataType, method, url } = component.__config__;
			if (dataType === 'dynamic' && method && url) {
				this.setLoading(component, true);
				this.$axios({
					method,
					url,
				}).then(resp => {
					this.setLoading(component, false);
					this.setRespData(component, resp.data);
				});
			}
		},
		setLoading(component, val) {
			const { directives } = component;
			if (Array.isArray(directives)) {
				const t = directives.find(d => d.name === 'loading');
				if (t) t.value = val;
			}
		},
		activeFormItem(currentItem) {
			this.activeData = currentItem;
			this.activeId = currentItem.__config__.formId;
		},
		onEnd(obj) {
			if (obj.from !== obj.to) {
				this.fetchData(tempActiveData);
				this.activeData = tempActiveData;
				this.activeId = this.idGlobal;
			}
		},
		addComponent(item) {
			const clone = this.cloneComponent(item);
			this.fetchData(clone);
			this.drawingList.push(clone);
			this.activeFormItem(clone);
		},
		cloneComponent(origin) {
			const clone = deepClone(origin);
			const config = clone.__config__;
			config.span = this.formConf.span; // 生成代码时，会根据span做精简判断
			this.createIdAndKey(clone);
			clone.placeholder !== undefined && (clone.placeholder += config.label);
			tempActiveData = clone;
			return tempActiveData;
		},
		createIdAndKey(item) {
			const config = item.__config__;
			config.formId = ++this.idGlobal;
			config.renderKey = `${config.formId}${+new Date()}`; // 改变renderKey后可以实现强制更新组件
			if (config.layout === 'colFormItem') {
				item.__vModel__ = `field${this.idGlobal}`;
			} else if (config.layout === 'rowFormItem') {
				config.componentName = `row${this.idGlobal}`;
				!Array.isArray(config.children) && (config.children = []);
				delete config.label; // rowFormItem无需配置label属性
			}
			if (Array.isArray(config.children)) {
				config.children = config.children.map(childItem => this.createIdAndKey(childItem));
			}
			return item;
		},
		// 获得表单数据
		AssembleFormData() {
			this.formData = {
				fields: deepClone(this.drawingList),
				...this.formConf,
			};
		},
		save() {
			// this.AssembleFormData()
			// console.log(this.formData)
			this.$refs['form'].validate(valid => {
				if (!valid) {
					return;
				}
				const form = {
					conf: JSON.stringify(this.formConf), // 表单配置
					fields: this.encodeFields(), // 表单项的数组
					...this.form, // 表单名等
				};
				// 修改的提交
				if (this.form.id != null) {
					updateForm(form).then(response => {
						this.$modal.msgSuccess('修改成功');
						this.close();
					});
					return;
				}
				// 添加的提交
				createForm(form).then(response => {
					this.$modal.msgSuccess('新增成功');
					this.close();
				});
			});
		},
		/** 关闭按钮 */
		close() {
			this.$tab.closeOpenPage({ path: '/bpm/manager/form' });
		},
		encodeFields() {
			const fields = [];
			this.drawingList.forEach(item => {
				fields.push(JSON.stringify(item));
			});
			return fields;
		},
		generate(data) {
			const func = this[`exec${titleCase(this.operationType)}`];
			this.generateConf = data;
			func && func(data);
		},
		execRun(data) {
			this.AssembleFormData();
			this.drawerVisible = true;
		},
		execDownload(data) {
			const codeStr = this.generateCode();
			const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' });
			saveAs(blob, data.fileName);
		},
		execCopy(data) {
			document.getElementById('copyNode').click();
		},
		empty() {
			this.$confirm('确定要清空所有组件吗？', '提示', { type: 'warning' }).then(() => {
				this.drawingList = [];
				this.idGlobal = 100;
			});
		},
		drawingItemCopy(item, list) {
			let clone = deepClone(item);
			clone = this.createIdAndKey(clone);
			list.push(clone);
			this.activeFormItem(clone);
		},
		drawingItemDelete(index, list) {
			list.splice(index, 1);
			this.$nextTick(() => {
				const len = this.drawingList.length;
				if (len) {
					this.activeFormItem(this.drawingList[len - 1]);
				}
			});
		},
		generateCode() {
			const { type } = this.generateConf;
			this.AssembleFormData();
			const script = vueScript(makeUpJs(this.formData, type));
			const html = vueTemplate(makeUpHtml(this.formData, type));
			const css = cssStyle(makeUpCss(this.formData));
			return beautifier.html(html + script + css, beautifierConf.html);
		},
		showJson() {
			this.AssembleFormData();
			this.jsonDrawerVisible = true;
		},
		download() {
			this.dialogVisible = true;
			this.showFileName = true;
			this.operationType = 'download';
		},
		run() {
			this.dialogVisible = true;
			this.showFileName = false;
			this.operationType = 'run';
		},
		copy() {
			this.dialogVisible = true;
			this.showFileName = false;
			this.operationType = 'copy';
		},
		tagChange(newTag) {
			newTag = this.cloneComponent(newTag);
			const config = newTag.__config__;
			newTag.__vModel__ = this.activeData.__vModel__;
			config.formId = this.activeId;
			config.span = this.activeData.__config__.span;
			this.activeData.__config__.tag = config.tag;
			this.activeData.__config__.tagIcon = config.tagIcon;
			this.activeData.__config__.document = config.document;
			if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
				config.defaultValue = this.activeData.__config__.defaultValue;
			}
			Object.keys(newTag).forEach(key => {
				if (this.activeData[key] !== undefined) {
					newTag[key] = this.activeData[key];
				}
			});
			this.activeData = newTag;
			this.updateDrawingList(newTag, this.drawingList);
		},
		updateDrawingList(newTag, list) {
			const index = list.findIndex(item => item.__config__.formId === this.activeId);
			if (index > -1) {
				list.splice(index, 1, newTag);
			} else {
				list.forEach(item => {
					if (Array.isArray(item.__config__.children))
						this.updateDrawingList(newTag, item.__config__.children);
				});
			}
		},
		refreshJson(data) {
			this.drawingList = deepClone(data.fields);
			delete data.fields;
			this.formConf = data;
		},
	},
};
</script>

<style lang="scss">
@import '@/styles/home';
</style>
